package de.kardass.jannobatch.manager;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

public abstract class ReflectUtil {

	private static final String PROCESS = "process";
	private static final String WRITE = "write";

	public static Class<?> getProcessMethodsReturnType(Class<?> processorClass, Class<?> processMethodParameter) {
		Method method = getProcessMethodWithParameter(processorClass, processMethodParameter);
		return method == null ? null : method.getReturnType();
	}
	
	public static Method getProcessMethodWithParameter(Class<?> processorClass, Class<?> processMethodParameter) {
		return getMethodWithParameter(processorClass, PROCESS, processMethodParameter);
    }

	public static Method getWriteMethodWithParameter(Class<?> clazz, Class<?> methodParameter) {
		return getMethodWithParameter(clazz, WRITE, methodParameter);
    }

	private static Method getMethodWithParameter(Class<?> clazz, String methodName, Class<?> methodParameter) {
		Method result = null;
		final List<Method> methods = getMethodsByName(clazz, methodName);
		for (Method aMethod : methods) {
			if (aMethod.getParameterTypes().length == 1 && methodParameter.equals(aMethod.getParameterTypes()[0])) {
				result = aMethod;
			}
        }
		// if no method found with given parameter, maybe processMethodParameter's super type ist used
		if (result == null) {
			Class<?> superClass = methodParameter.getSuperclass();
			if (superClass != null) {
				result = getMethodWithParameter(clazz, methodName, superClass);
			}
		}
		return result;
    }

	public static List<Method> getMethodsByName(Class<?> clazz, String methodName) {
		List<Method> result = new ArrayList<Method>();
		final Method[] methods = clazz.getMethods();
		Method aMethod;
		for (int i = 0; i < methods.length; i++) {
			aMethod = methods[i];
			if (aMethod.getName().equals(methodName) && !Modifier.isAbstract(aMethod.getModifiers())) {
				result.add(aMethod);
			}
		}
		return result;
	}

	/**
	 * Checks whether superClass is a super class of subClass. 
	 * Means "subClass extends superClass" somehow.
	 * @param superClass
	 * @param subClass
	 * @return
	 */
	public static boolean isSuperClassOf(Class<?> superClass, Class<?> subClass) {
		final Class<?> subClassSuperClass = subClass.getSuperclass();
		if (subClassSuperClass != null && subClassSuperClass.equals(superClass)) {
			return true;
		} else if (subClassSuperClass != null && !subClassSuperClass.equals(subClass)) {
			return isSuperClassOf(superClass, subClassSuperClass);
		} else {
			return false;
		}
	}

}
